﻿
using Alternet.Scripter;
using Alternet.Scripter.Debugger;
using Microsoft.Build.Framework.XamlTypes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Channels;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OrBitDataModule;
using OrBitDataModule.Adaptor;
using OrBitDataModule.Base;
using OrBitDataModule.Common;
using OrBitDataModule.Interface;
using OrBitDataModule.Models;
using System.Text.RegularExpressions;

namespace ScriptRunner
{
    public class Scripter : IScriptRemoteControl
    {
        private ScriptFinishedDelegate onScriptFinished;

        private string UniqueId = string.Empty;
        private string UserName = string.Empty;
        private string Password = string.Empty;
        private string ipcPortName = null;
        private string ipcObjectUri = null;
        private volatile bool scriptRunning;
        private object scriptObj = null;
        private ScriptRun scriptRun;
        private IChannel channel;
        private TaskService Service = null;

        #region 调试运行部分
        public Scripter(string uniqueId, string userName, string password, string PortName, string ObjectUri)
        {
            Service = new TaskService();
            this.UniqueId = uniqueId;
            this.UserName = userName;
            this.Password = password;
            this.ipcPortName = PortName;
            this.ipcObjectUri = ObjectUri;

            this.scriptRun = new ScriptRun();
            this.scriptRun.AssemblyKind = ScriptAssemblyKind.DynamicLibrary;
            this.scriptRun.ScriptMode = ScriptMode.Debug;

            channel = DebuggerCommunication.StartServer(this, ipcPortName, ipcObjectUri);
            scriptRun.ScriptLanguage = ScriptLanguage.CSharp;
            scriptRun.ScriptMode = ScriptMode.Debug;
            scriptRun.ScriptHost.GenerateModulesOnDisk = true;
            scriptRun.ScriptHost.ModulesDirectoryPath = AppDomain.CurrentDomain.BaseDirectory;

            scriptRun.ScriptSource.WithDefaultReferences();

            ClearTempDirectory();
            ScriptCompile();
            Service.OnMessage += (s, e) => { Console.WriteLine(e); };
            Service.OnTaskStatusChange+= (s, e) => { Console.WriteLine(Enum.GetName(typeof(TaskStatus),e.Status)); };
        }
        public void ScriptCompile()
        {
            string scriptPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scripter.cs");
            scriptRun.ScriptSource.FromScriptFile(scriptPath);
            //默认添加数据模块dll的引用
            AddReference(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "OrBitDataModule.dll"));
            AddReference("System.Data.dll");
            AddReference("System.Windows.Forms.dll");
            AddReference("Newtonsoft.Json.dll");
            AddReference("netstandard.dll");
            AddReference("System.Text.Json.dll");
            AddReference("System.Text.Encodings.Web.dll");
            AddReference("System.Text.Encoding.CodePages.dll");
            AddLocalReference();
            if (!CompileScriptIfNeeded())
            {
                MessageBox.Show(string.Join("\r\n", scriptRun.ScriptHost.CompilerErrors.Select(x => x.ToString()).ToArray()));
                return;
            }
        }
        public void CompileScript(ScriptCompiledDelegate onScriptCompiled)
        {
            Task.Run(() =>
            {
                var success = CompileScriptIfNeeded();
                InvokeOnScriptCompiled(
                    onScriptCompiled,
                    new ScriptCompilationResult
                    {
                        IsSuccessful = success,
                        TargetAssemblyName = scriptRun.ScriptHost.ExecutableModulePath,
                        Errors = scriptRun.ScriptHost.CompilerErrors,
                    });
            });
        }
        private void InvokeOnScriptCompiled(ScriptCompiledDelegate onScriptCompiled, ScriptCompilationResult result)
        {
            if (onScriptCompiled != null)
            {
                try
                {
                    onScriptCompiled(result);
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());
                }
            }
        }

        public bool IsScriptRunning()
        {
            return scriptRunning;
        }

        public void StartScript(ScriptFinishedDelegate onScriptFinished)
        {
            this.onScriptFinished = onScriptFinished;
            if (!CompileScriptIfNeeded())
            {
                MessageBox.Show(string.Join("\r\n", scriptRun.ScriptHost.CompilerErrors.Select(x => x.ToString()).ToArray()));
                return;
            }
            scriptRunning = true;
            Start();
        }

        public void StopScript()
        {
            Service.StopTask(UniqueId);
            scriptRunning = false;
            if (onScriptFinished != null)
                InvokeOnScriptFinished(onScriptFinished);
            onScriptFinished = null;

            if (channel != null)
            {
                DebuggerCommunication.StopServer(channel);
                channel = null;
            }
        }
        private bool CompileScriptIfNeeded()
        {
            if (!scriptRun.Compiled)
            {
                scriptRun.ScriptHost.AssemblyFileName = Guid.NewGuid().ToString("N") + ".dll";
                return scriptRun.Compile();
            }
            return true;
        }
        private void InvokeOnScriptFinished(ScriptFinishedDelegate onScriptFinished)
        {
            if (onScriptFinished != null)
            {
                try
                {
                    onScriptFinished();
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());
                }
            }
        }
        private void ClearTempDirectory()
        {
            try
            {
                var path = scriptRun.ScriptHost.ModulesDirectoryPath;
                if (Directory.Exists(path))
                {
                    string pattern = @"^[a-zA-Z0-9]{32}\.(dll|pdb)$";
                    Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);

                    // 获取目录中的所有文件
                    string[] files = Directory.GetFiles(path);

                    foreach (string file in files)
                    {
                        string fileName = Path.GetFileName(file);
                        if (regex.IsMatch(fileName))
                        {
                            try
                            {
                                File.Delete(file);
                            }
                            catch
                            {
                            }
                        }
                    }
                }
            }
            catch
            {
            }
        }

        //public void RunMethod()
        //{
        //    Assembly assembly = scriptRun.ScriptHost.ScriptAssembly;

        //    if (assembly == null)
        //    {
        //        MessageBox.Show("assembly is null");
        //        return;
        //    }

        //    MessageBox.Show("2");
        //    Type type = FindDataBaseType(assembly, "DataBase");
        //    scriptObj = Activator.CreateInstance(type);
        //    type.BaseType.InvokeMember("Init", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, scriptObj, new object[] { "ExampleName" });
        //}

        public void AddReference(string dllFilePath)
        {
            scriptRun.ScriptSource.References.Add(dllFilePath);
        }
        private void AddLocalReference()
        {
            string directory = AppDomain.CurrentDomain.BaseDirectory;
            foreach (string file in Directory.GetFiles(directory, "*.dll").Where(f => f.Length != 36))
            {
                try
                {
                    if (Path.GetFileName(file).StartsWith("System.") && Path.GetFileName(file) != "System.EnterpriseServices.Wrapper.dll")
                    {
                        AddReference(Path.GetFileName(file));
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"加载 {Path.GetFileName(file)} 时出错：{ex.Message}");
                }
            }
        }
        #endregion

        #region 脚本运行部分

        private async void Start()
        {
            List<TaskModel> list = await Service.InitAllTask();
            TaskModel taskModel = Service.GetTaskModel(list, UniqueId);
            if (taskModel != null)
            {
                taskModel.DataTask.IsScripDebug = true;
                //获取脚本编译对象
                Assembly assembly = scriptRun.ScriptHost.ScriptAssembly;
                Type type = FindDataBaseType(assembly, "ScriptBase");
                scriptObj = Activator.CreateInstance(type);
                ScriptBase script = scriptObj as ScriptBase;
                IContext context = new Context(taskModel.DataTask);
                script.GetType().BaseType.InvokeMember("Init", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, script, new object[] { context });
                taskModel.DataTask.Script = script;
                Service.StartTask(UniqueId);
            }
            else      
                MessageBox.Show("taskModel is null");
        }

        /// <summary>
        /// 找对象
        /// </summary>
        /// <param name="assembly"></param>
        /// <param name="baseTypeName"></param>
        /// <returns></returns>
        private Type FindDataBaseType(Assembly assembly, string baseTypeName) => assembly.GetTypes().FirstOrDefault(t => !t.IsAbstract && t.IsClass && t.BaseType != null && t.BaseType.Name == baseTypeName);

        #endregion
    }
}
